#include "transfile.h"
#define FILESIZE 104857600
#define RESUME_REQUEST 20
#define TRANSFER_END 21
#define RESUME_RESPONSE 22
int recvn(int sockfd,void *buf,int n){
    //void *不能解引用和偏移
    char *p = (char *)buf;
    int cursize = 0;//记录已经收到的数量
    while(cursize<n){
        ssize_t sret = recv(sockfd,p+cursize,n-cursize,0);
        cursize += sret;
    }
    return 0;
}

int sendFile(int sockfd, char *fileName) {
    train_t train;

    // 打开文件
    int fd = open(fileName, O_RDONLY);
    ERROR_CHECK(fd, -1, "open");

    // 获取文件大小
    struct stat statbuf;
    fstat(fd, &statbuf);
    off_t fileSize = statbuf.st_size;

    // 1. 发送文件名
    bzero(&train, sizeof(train));
    train.type = 0;
    train.length = strlen(fileName);
    memcpy(train.data, fileName, train.length);
    send(sockfd, &train, sizeof(train.type) + sizeof(train.length) + train.length, MSG_NOSIGNAL);

    // 2. 接收断点请求
    off_t offset = 0;
    recvn(sockfd, &train.type, sizeof(train.type));
    if (train.type == RESUME_REQUEST) {
        recvn(sockfd, &train.length, sizeof(train.length));
        recvn(sockfd, train.data, train.length);
        memcpy(&offset,train.data,train.length);
        printf("Resume transfer from offset: %ld\n", offset);
    }else if(train.type==RESUME_RESPONSE){
        recvn(sockfd,&train.length,sizeof(train.length));
    }
    lseek(fd, offset, SEEK_SET);
    off_t remainingSize = fileSize - offset;

    // 3. 发送剩余文件大小
    bzero(&train, sizeof(train));
    train.type = 0;
    train.length = sizeof(remainingSize);
    memcpy(train.data, &remainingSize, train.length);
    send(sockfd, &train, sizeof(train.type) + sizeof(train.length) + train.length, MSG_NOSIGNAL);

    // 4. 文件内容发送
    if (remainingSize >= FILESIZE) {
        // 大文件：sendfile
        while (remainingSize > 0) {
            ssize_t sent = sendfile(sockfd, fd, &offset, remainingSize);
            if (sent == -1) {
                perror("sendfile");
                break;
            }
            remainingSize -= sent;
        }
    } else {
        // 小火车传输
        while (1) {
            bzero(&train, sizeof(train));
            train.type = 0;
            ssize_t sret = read(fd, train.data, sizeof(train.data));
            if (sret <= 0) break;

            train.length = sret;
            if (send(sockfd, &train, sizeof(train.type) + sizeof(train.length) + train.length, MSG_NOSIGNAL) <= 0) {
                perror("send");
                break;
            }
        }
        printf("read over\n");
        // 发送结束标记
        bzero(&train, sizeof(train));
        train.type = TRANSFER_END;
        char arr[] = "sendfile over";
        train.length = strlen(arr);
        memcpy(train.data,arr,train.length);
        send(sockfd, &train, (sizeof(train.type) + sizeof(train.length)+train.length), MSG_NOSIGNAL);
        printf("send TRANSFER_END over\n");
    }
    close(fd);
    return 0;
}
int recvFile(int sockfd,long *fileSize) {
    train_t train;

    // 1. 接收文件名
    char fileName[1024]={0};
    recvn(sockfd, &train.type, sizeof(train.type));
    recvn(sockfd, &train.length, sizeof(train.length));
    recvn(sockfd, train.data, train.length);
    memcpy(fileName, train.data, train.length);

    // 2. 判断本地文件是否存在
    off_t offset = 0;
    struct stat statbuf;
    if (stat(fileName, &statbuf) == 0) {
        offset = statbuf.st_size;
        bzero(&train, sizeof(train));
        train.type = RESUME_REQUEST;
        train.length = sizeof(offset);
        memcpy(train.data, &offset, train.length);
        send(sockfd, &train, sizeof(train.type) + sizeof(train.length) + train.length, MSG_NOSIGNAL);
    } else {
        bzero(&train, sizeof(train));
        train.type = RESUME_RESPONSE;
        train.length = 0;
        send(sockfd, &train, sizeof(train.type) + sizeof(train.length), MSG_NOSIGNAL);
    }

    // 3. 接收剩余文件大小
    off_t remainingSize;
    recvn(sockfd, &train.type, sizeof(train.type));
    recvn(sockfd, &train.length, sizeof(train.length));
    recvn(sockfd, train.data, train.length);
    memcpy(&remainingSize,train.data,train.length);
*fileSize=remainingSize;
    // 4. 打开文件
    int fd = open(fileName, O_WRONLY | O_CREAT, 0666);
    ERROR_CHECK(fd, -1, "open");

    // 5. 文件接收
    if (remainingSize >= FILESIZE) {
        // 大文件 mmap 方式

        off_t totalSize = offset + remainingSize;
        ftruncate(fd, totalSize);

        long pageSize = sysconf(_SC_PAGE_SIZE);
        off_t mapOffset = offset & ~(pageSize - 1);
        off_t delta = offset - mapOffset;

        char *p = (char *)mmap(NULL, remainingSize + delta, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mapOffset);
        if (p == MAP_FAILED) {
            perror("mmap");
            close(fd);
            return -1;
        }

        recvn(sockfd, p + delta, remainingSize);
        munmap(p, remainingSize + delta);
    } else {
        // 小火车
        lseek(fd, offset, SEEK_SET);
        while (remainingSize > 0) {
            int sret = recvn(sockfd, &train.type, sizeof(train.type));
            printf("sret = %d\n",sret);
            sret = recvn(sockfd, &train.length, sizeof(train.length));
            printf("sret = %d\n",sret);
            sret = recvn(sockfd, train.data, train.length);
            printf("sret = %d\n",sret);
            write(fd, train.data, train.length);
            remainingSize -= train.length;
        }
        //在收取数据
        bzero(&train,sizeof(train));
        printf("recv oover\n");
        recvn(sockfd,&train.type,sizeof(train.type));
        printf("train.type1 = %d\n",train.type);
        recvn(sockfd,&train.length,sizeof(train.length));
        printf("train.length = %d\n",train.length);
        recvn(sockfd,train.data,train.length);
        printf("train.data = %s\n",train.data);
    }

    close(fd);
    return 0;
}

